﻿using System;
using System.Runtime.CompilerServices;
using UnityEngine;

public class CTaskUpdate : MonoBehaviour
{
	public event Action onUpdate;
	private void Update()
	{
		onUpdate?.Invoke();
	}
}

public class CompleteTask : INotifyCompletion
{
	public CompleteTask GetAwaiter() => this;
	public void OnCompleted(Action continuation) { }
	public bool IsCompleted => true;
	public object GetResult() { return null; }
}

public class CompleteTask<T> : INotifyCompletion
{
	public CompleteTask<T> GetAwaiter() => this;
	public void OnCompleted(Action continuation) { }
	public bool IsCompleted => true;
	public T GetResult() { return default(T); }
}

[AsyncMethodBuilder(typeof(CTaskMethodBuilder))]
public class CTask : ICriticalNotifyCompletion
{
	private static CTaskUpdate updateInstance;
	private bool isComplated;
	private Action callback;
	private float timer;
	private bool isForWait;
	private Type resultType;
	private object result;
	private bool hasRegisterUpdate;

	private CTask() { }

	public static CTask Create(Type resultType = null)
	{
		if (!updateInstance)
		{
			updateInstance = GameObject.FindObjectOfType<CTaskUpdate>();
			if (!updateInstance)
			{
				GameObject go = new GameObject("[CTaskUpdate]");
				GameObject.DontDestroyOnLoad(go);
				updateInstance = go.AddComponent<CTaskUpdate>();
			}
		}
		CTask task = new CTask();
		task.resultType = resultType;
		return task;
	}

	public static CompleteTask Completed { get { return new CompleteTask(); } }

	public CTask GetAwaiter() => this;

	public bool IsCompleted => isComplated;

	public object GetResult() { return result; }

	public object Result { get { return result; } }

	public Type GetResultType() { return resultType; }

	public void Invoke() { }

	public void UnsafeOnCompleted(Action continuation)
	{
		this.callback = continuation;
	}

	public void OnCompleted(Action continuation)
	{
		this.callback = continuation;
	}

	public void Update()
	{
		if (timer > 0 && timer < Time.time)
		{
			timer = 0;
			SetResult();
		}
	}

	public void SetResult(object obj = null)
	{
		isComplated = true;
		this.result = obj;
		callback?.Invoke();
		if (!isForWait)
		{
			Dispose();
		}
	}

	public void Dispose()
	{
		timer = 0;
		updateInstance.onUpdate -= Update;
		hasRegisterUpdate = false;
		callback = null;
	}

	public static CTask Wait(float seconds)
	{
		CTask task = CTask.Create();
		task.timer = Time.time + seconds;
		task.isForWait = true;
		if (!task.hasRegisterUpdate)
		{
			updateInstance.onUpdate += task.Update;
			task.hasRegisterUpdate = true;
		}
		return task;
	}
}

[AsyncMethodBuilder(typeof(CTaskMethodBuilder<>))]
public class CTask<T> : ICriticalNotifyCompletion
{
	private bool isComplated;
	private Action callback;
	private T result;

	private CTask() { }

	public static CTask<T> Create()
	{
		CTask<T> task = new CTask<T>();
		task.isComplated = false;
		return task;
	}

	public CTask<T> GetAwaiter() => this;

	public bool IsCompleted => isComplated;

	public void Invoke() { }

	public T GetResult() { return result; }

	public T Result { get { return result; } }

	public void UnsafeOnCompleted(Action continuation)
	{
		this.callback = continuation;
	}

	public void OnCompleted(Action continuation)
	{
		this.callback = continuation;
	}

	public void SetResult(T t)
	{
		isComplated = true;
		this.result = t;
		callback?.Invoke();
		callback = null;
	}

	public Type GetResultType()
	{
		return result.GetType();
	}
}
